 /*
    *  Copyright 2004 Thevert Liu  <thevert.liu@gmail.com>
    *
    *  Licensed under the Apache License, Version 2.0 (the "License");
    *  you may not use this file except in compliance with the License.
    *  You may obtain a copy of the License at
    *
    *      http://www.apache.org/licenses/LICENSE-2.0
    *
   *  Unless required by applicable law or agreed to in writing, software
  *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   */
#include <xstring.h>
#include <sstream>
#include <cstring>

namespace x2
{

std::wstring& UTF8Helper::UTF8ToUTF16(const std::string& str, std::wstring& dest)
{
    size_t cur, seek, len=str.length() ;
    dest.resize(len) ;
    wchar_t* buf = (wchar_t*) dest.c_str();
    const char * sstr= str.c_str() ;
    for (cur=0; cur<len; ) {
        seek=0;
        *buf++ = read_char(sstr+cur, seek) ;
        cur += seek ;
    }
    dest.resize(buf-dest.c_str()) ;
    return dest ;
}

std::string& UTF8Helper::UTF16ToUTF8(const std::wstring& str, std::string& dest)
{
    dest.resize(0);
    // reserve the buffer size to avoid the realloc.
    dest.reserve(str.length()*2) ;

    const wchar_t * t = str.c_str() ;

    size_t cur, len=str.length() ;

    for (cur=0; cur<len; cur++) {
        write_char(*t++, dest) ;
    }

    return dest ;
}


wchar_t UTF8Helper::read_char(const char* str, size_t& seek)
{
    unsigned char * t = (unsigned char *)str ;
    unsigned char ch = *t ;

    /// 0xc0 -> b1110 0000 0x80->b1000 0000, skip the 10xxxxxx bytes.
    /// (forgeted by the last char). find the start flag of one char
    while(ch && (ch & 0xc0) == 0x80)
    {
        ch = *(++t) ;
    }


    /// if 0xxxxxxx to 0000 0000-0000 007F
    if (ch < 0x80) {
        seek = 1 ;
        return ch ;
    }

    /// 11110xxx detect how many bytes should use.
    int tailBytes = 0;
    int mask = 0x40; //b0100 0000, max bytes is 6.
    while (ch & mask) // find the first 0 after 1111...
    {
        tailBytes++;
        mask >>= 1;
    }

    /// 0x3f->b0011 1111, remove the mask part of ch, as: from 11110xxx to 00000xxx,
    ///  get the height byte value.
    wchar_t result = ch & (0x3f >> tailBytes);

    while (tailBytes--)
    {
        result <<= 6;
        result |= *(++t) & 0x3f;
    }

    seek=(char*)t-str+1 ;
    return result;
}

void UTF8Helper::write_char(wchar_t c, std::string& to)
{
    size_t len = to.length() ;
    if (c < 0x80) {
        to.resize(len+1) ;
        *((char*)to.data()+len)=c ;
    } else if (c < 0x800) {
        to.resize(len+2) ;
        char *t = (char*)to.data() + len ;
        *t++ = 0xc0 |  (c >> 6) ;
        *t = 0x80 |  (c & 0x3f) ;
    } else { // else ignore, out of range
    }
}

size_t Tokens::find(const std::string& s)
{
    for (size_t i=0; i<size(); i++) {
        if (at(i) == s) {
            return i ;
        }
    }

    return std::string::npos ;
}


bool string::from(bool v) 
{
    if (v) {
        *this = "true" ;
    } else {
        *this = "false" ;
    }
    return true ;
}

bool string::from(int v) 
{
    std::stringstream os ;
    os << v ;
    *this = os.str() ;
    return true ;
}

bool string::from_hex(unsigned int v) 
{
    char buf[64] ;
    snprintf(buf, 64, "0x%x", v) ;
    *this = buf ;
    return true ;
}

bool string::from(double v) 
{
    std::stringstream os ;
    os << v ;
    *this = os.str() ;
    return true ;
}

bool string::from(float v) 
{
    std::stringstream os ;
    os << v ;
    *this = os.str() ;
    return true ;
}


bool string::to(bool v) const
{
    if (*this == "true") {
        v = true ;
    } else if (*this=="false") {
        v = false ;
    } else {
        return false ;
    }
    return true ;
}

bool string::to(int v) const
{
    std::stringstream ss(*this) ;
    ss >> v ;
    return true ;
}

bool string::to(double v) const
{
    std::stringstream ss(*this) ;
    ss >> v ;
    return true ;
}

bool string::to(float v) const
{
    std::stringstream ss(*this) ;
    ss >> v ;
    return true ;
}

bool string::to_hex(unsigned int v) const 
{
    string hexchars =  "ABCDEFabcdef0123456789" ;
    string tmp = *this ;
    if (!tmp) {
        return false ;
    }

    size_t start = 0;
    size_t end = find_first_not_of(hexchars);

    if (end == start) {
        return false;
    }

    if (start > 0 || end < tmp.length()) {
        tmp = tmp.substr(start, end);
    }
    
    sscanf(tmp.c_str(), "%X", &v) ;    
    
    return true ;
}

bool string::is_digit(char c) 
{
    static string digitchars =  "0123456789" ;
    return digitchars.has(c) ;
}

bool string::is_hex(char c) 
{
    static string hexchars =  "ABCDEFabcdef0123456789" ;
    return hexchars.has(c) ;
}

bool string::is_space(char c) 
{
    return c == ' ' || c == '\n' || c == '\r' || c == '\t';
}


bool string::starts_with(const std::string& s) const 
{
    return (find(s) == 0) ;
}


string& string::to_upper() 
{
    string &ret = *this ;

    for (size_t i=0;i<ret.size(); i++)
    {
        ret[i] = toupper(ret[i]) ;
    }
    return ret ;
}

string& string::to_lower() 
{
    string &ret = *this ;

    for (size_t i=0;i<ret.size();i++)
    {
        ret[i] = tolower(ret[i]) ;
    }
    
    return ret ;
}

bool string::read(std::istream& is) 
{
    if (is.eof() || !is.good()) {
        return false;
    }

    size_t len = 0 ;
    char buf[256] ;
    while(!is.eof()) {
        is.read(buf, 256) ;
        len = is.gcount();
        if (len!=0) {
            append(buf, len) ;
        }
    }
    return true ;
}

bool string::iequals(const std::string& s) const 
{
    return strcasecmp(c_str(), s.c_str()) == 0 ;
}

bool string::has(char c) const 
{
    return find(c) != std::string::npos ;
}

bool string::has(const std::string& s) const 
{
    return find(s) != std::string::npos ;
}

bool string::hasi(const std::string& s) const
{
    string f = *this ;
    string d = s ;
    f.to_lower() ;
    d.to_lower() ;
    return f.has(d) ;
}


string& string::to_utf8(const std::wstring& ws) 
{
    UTF8Helper::UTF16ToUTF8(ws, *this) ;
    return *this ;
}

std::wstring& string::to_utf16(std::wstring& ws) const 
{
    return UTF8Helper::UTF8ToUTF16(*this, ws) ;
}


Tokens string::tokens(const std::string& whitechars) const 
{
    Tokens vtokens ;
    tokens(vtokens, whitechars) ;
    return vtokens ;
}

bool string::tokens(Tokens& vtokens, const std::string& whitechars) const 
{
    const s3::string& s = *this ;
    size_type t=0, f=0 ;
    do {
        f = s.find_first_not_of(whitechars, t) ;
        if (f==npos) break ;
        t = s.find_first_of(whitechars, f) ;
        vtokens.push_back(s.substr(f, t-f)) ;
    } while(t!=npos) ;

    return !vtokens.empty() ;
}

bool string::split(const std::string& s, std::string& left, std::string& right, const std::string& whitechars) const  
{
    size_type f = find_first_not_of(whitechars) ;
    if (f==npos) {
        return false ;
    }

    size_type t = find_first_of(whitechars) ;
    if (t==npos) {
        return false ;
    }

    size_type f2 = find_first_not_of(whitechars, t) ;

    if (f2 == npos) return false ;

    left = substr(f, t) ;

    right = substr(f2) ;

    return true ;
}


} ;


